from symbide import ModuleManager, LogManager, PATHS
from os import listdir
import os.path
from xml.dom import minidom
from StringIO import StringIO

import gtk, gobject

class Image(gtk.EventBox):
    '''
    This widget capsulates Images.
    '''
        
    PIL_write_formats = (('eps', 'Encapsulated PostScript'),
                         ('gif', 'Graphics Interchange Format'),
                         ('im', 'IM format (IFUNC library)'),
                         ('msp', 'Microsoft Paint'),
                         ('pcx', 'Picture exchange'),
                         ('pdf', 'Portable Document Format'),
                         ('ppm', 'Portable Pixmap'),
                         ('xbm', 'X bitmap'),
                         )
    
    def __init__(self, path=None, show=True):
        gtk.EventBox.__init__(self)
        self.show()

        self.path = path
        
        self.connect('enter-notify-event', self._event_mouseEnter)
        self.connect('leave-notify-event', self._event_mouseLeave)
        self.connect('button-press-event', self._event_mouseClick)
        
        self.menuitems = [('Save image as ..', self._event_saveAs),
                          ]
        
        self.image = gtk.Image()
        self.image.show()
        self.add(self.image)
        
        if show and path:
            self.showImage()
        
    def _event_mouseEnter(self, widget, event):
        '''
        Changes the curser to a plus when it enters.
        '''

        gtk.gdk.Window.set_cursor(self.window, gtk.gdk.Cursor(gtk.gdk.ARROW))
        
    def _event_mouseLeave(self, widget, event):
        '''
        Changes the cursor back to normal when it leaves.
        '''
        
        gtk.gdk.Window.set_cursor(self.window, gtk.gdk.Cursor(gtk.gdk.ARROW))            
        
    
    def _event_mouseClick(self, widget, event):
        '''
        Finds out which button is pressed and passes the signal on to
        the according methods.
        '''
        
        if event.button==1:
            return self._event_leftMouseClick(widget, event)
        elif event.button==2:
            return self._event_middleMouseClick(widget, event)
        elif event.button==3:
            return self._event_rightMouseClick(widget, event)
            
    def _event_leftMouseClick(self, widget, event):
        pass
    
    def _event_middleMouseClick(self, widget, event):
        pass
    
    def _event_rightMouseClick(self, widget, event):
        popupmenu = gtk.Menu()
        popupmenu.show()
        for caption, method in self.menuitems:
            menuitem = gtk.MenuItem(caption)
            menuitem.connect('activate', method)
            menuitem.show()
            popupmenu.append(menuitem)
            
        popupmenu.popup(None, None, None, event.button, event.time)
        return True
            
    def _event_saveAs(self, widget):
        '''
        Opens a File chooser dialog and asks the user for a path. Then calls the
        method saveAs. If an error occurs, a message dialog pops up.
        '''
        
        path = self.saveImageAsDialog()
        if not path:
            return
        else:
            path, format = path
        
        feedback = self.saveAs(path, format)
        
        if feedback:
            m=gtk.MessageDialog(parent=None,
                                flags=gtk.DIALOG_DESTROY_WITH_PARENT,
                                type=gtk.MESSAGE_ERROR, 
                                buttons=gtk.BUTTONS_OK,
                                message_format=feedback)
            m.run()
            m.destroy()
    
    def saveAs(self, path, format=None):
        '''
        Saves the image to path. The extension is used to determine the
        format to use. At the moment only png and jpeg are possible.
        '''
        #TODO: add more formats to save images
        #TODO: let the user set the compression of jgeg
        if not format:
            format = os.path.splitext(path)[1].strip('.')
        ext = ''
        mode = None
        
        for i in gtk.gdk.pixbuf_get_formats():
            if format in i['extensions']:
                if i['is_writable']:
                    ext = i['name']
                    mode = 'gtk'
                break
        
        if ModuleManager.hasModule('PIL') and not ext:
            for i in self.PIL_write_formats:
                if format==i[0]:
                    ext = format
                    mode = 'PIL'
                    break
        
        if not ext:
            return "Can't recognize this format"
        
        pixbuf=self.image.get_pixbuf()
        
        if mode=='gtk':
            try:
                pixbuf.save(path, ext)
                return None
            except Exception, e:
                return 'Error while saving image: %s'%e
        elif mode=='PIL':
            try:
                im = self._pixbuf2pil(pixbuf)
                im.save(path)
            except Exception, e:
                return 'Error while saving image: %s'%e
    
    def showImage(self):
        '''
        Loads the image from self.path and diplays it.
        '''
        
        if os.path.exists(self.path):
            try:
                pixbuf = gtk.gdk.pixbuf_new_from_file(self.path)
                self.image.set_from_pixbuf(pixbuf)
            except gobject.GError:
                if ModuleManager.hasModule('PIL'):
                    from PIL import Image as PIL_Image
                    try:
                        im = PIL_Image.open(self.path)
                        pixbuf = self._pil2pixbuf(im)
                        self.image.set_from_pixbuf(pixbuf)
                    except IOError:
                        LogManager.alert('Not able to handle this format')
                else:
                    LogManager.alert('Not able to handle this format')
        else:
            LogManager.alert("Can't find Image at this location: %s"%self.path)
    
    def _pixbuf2pil(self, pixbuf):
        #TODO: Find better way to save image as PIL formats
        #width, height = pixbuf.get_width(), pixbuf.get_height()
        #return PIL_Image.fromstring("RGB", (width, height), pixbuf.get_pixels())
        path = os.path.join(PATHS['temp_pictures'], 'pil_temp.png')
        pixbuf.save(path, 'png')
        from PIL import Image as PIL_Image
        return PIL_Image.open(path)
    
    def _pil2pixbuf(self, im):  
        f = StringIO()  
        im.save(f, "ppm")  
        contents = f.getvalue()  
        f.close()  
        loader = gtk.gdk.PixbufLoader("pnm")  
        loader.write(contents, len(contents))  
        pixbuf = loader.get_pixbuf()  
        loader.close()  
        return pixbuf
            
    def todom(self):
        '''
        Returns a DOM representation of the plot-object.
        '''
        
        DOM = minidom.Element('img')
        DOM.setAttribute('src', self.path)
        return DOM
    
    def toxml(self):
        '''
        Returns a xml representation of the plot-object.
        '''
        
        return self.todom().toxml()
    
    def destroy(self):
        '''
        Cleans up everything.
        '''
        
        gtk.EventBox.destroy(self)
        self.image.destroy()
        
    def saveImageAsDialog(self):
        fileDialog = gtk.FileChooserDialog(\
                        title='Save Image',
                        action=gtk.FILE_CHOOSER_ACTION_SAVE,                                            
                        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                 gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        filter1 = gtk.FileFilter()
        formats = []
        for i in gtk.gdk.pixbuf_get_formats():
            if i['is_writable']:
                formats.append((i['name'],i['description']))
        if ModuleManager.hasModule('PIL'):
            for i in self.PIL_write_formats:
                formats.append(i)
        filter1.set_name('All supported formats')
        filter1.add_pixbuf_formats()
        fileDialog.add_filter(filter1)
        
        for f in formats:
            filter = gtk.FileFilter()
            filter.add_pattern('*.%s'%f[0])
            filter.set_name('%s (%s)'%f)
            fileDialog.add_filter(filter)
        
        filter2 = gtk.FileFilter()
        filter2.add_pattern('*')
        filter2.set_name('All files')
        fileDialog.add_filter(filter2)
        
        response = fileDialog.run()
        path = fileDialog.get_filename()
        format = fileDialog.get_filter().get_name()
        if not format in formats:
            format = None
        fileDialog.destroy()
        if response != gtk.RESPONSE_OK:
            return False
        else:
            return path, format